package com.xseagull.gateway;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtTokenUtil {

	private static final String CLAIM_KEY_USERID = "userId";
	private static final String CLAIM_KEY_CREATED = "created";
	private static final String CLAIM_KEY_VERSION = "version";
	public static final String HEADER = "Authorization";
	public static final String SECRET = "fjct";
	public static final long EXPIRATION = 89280;
	public static final String TOKENHEAD = "Bearer ";
	public static final String VERSION = "1.0.0";

	public static String generateToken(Map<String, Object> claims) {
		return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate())
				.signWith(SignatureAlgorithm.HS512, SECRET).compact();
	}

	private static Date generateExpirationDate() {
		return new Date(System.currentTimeMillis() + EXPIRATION * 1000);
	}

	public String getUsernameFromToken(String token) {
		String username;
		try {
			final Claims claims = getClaimsFromToken(token);
			username = claims.getSubject();
		} catch (Exception e) {
			username = null;
		}
		return username;
	}

	public Date getCreatedDateFromToken(String token) {
		Date created;
		try {
			final Claims claims = getClaimsFromToken(token);
			created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
		} catch (Exception e) {
			created = null;
		}
		return created;
	}

	public static String getUserIdFromToken(String token) {
		String userId;
		try {
			final Claims claims = getClaimsFromToken(token);
			userId = claims.get(CLAIM_KEY_USERID).toString();
		} catch (Exception e) {
			userId = null;
		}
		return userId;
	}
	
	public static String getVersionFromToken(String token) {
        String currVersion;
        try {
            final Claims claims = getClaimsFromToken(token);
            currVersion = claims.get(CLAIM_KEY_VERSION).toString();
        } catch (Exception e) {
        	currVersion = null;
        }
        return currVersion;
    }

	public static Date getExpirationDateFromToken(String token) {
		Date expiration;
		try {
			final Claims claims = getClaimsFromToken(token);
			expiration = claims.getExpiration();
		} catch (Exception e) {
			expiration = null;
		}
		return expiration;
	}

	private static Claims getClaimsFromToken(String token) {
		Claims claims;
		try {
			claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
		} catch (Exception e) {
			claims = null;
		}
		return claims;
	}

	private static Boolean isTokenExpired(String token) {
		final Date expiration = getExpirationDateFromToken(token);
		return expiration.before(new Date());
	}

	private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
		return (lastPasswordReset != null && created.before(lastPasswordReset));
	}

	public static AccessToken generateToken(String userId) {
		Map<String, Object> claims = new HashMap<>();
		claims.put(CLAIM_KEY_USERID, userId);
		claims.put(CLAIM_KEY_CREATED, new Date());
		claims.put(CLAIM_KEY_VERSION, VERSION);
		AccessToken accessToken = new AccessToken();
		accessToken.setAccess_token(generateToken(claims));
		accessToken.setToken_type(TOKENHEAD);
		accessToken.setExpires_in(EXPIRATION);
		accessToken.setUserId(userId);
		return accessToken;
	}

	public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
		final Date created = getCreatedDateFromToken(token);
		return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset) && !isTokenExpired(token);
	}

	public String refreshToken(String token) {
		String refreshedToken;
		try {
			final Claims claims = getClaimsFromToken(token);
			claims.put(CLAIM_KEY_CREATED, new Date());
			refreshedToken = generateToken(claims);
		} catch (Exception e) {
			refreshedToken = null;
		}
		return refreshedToken;
	}

	public static Boolean validateToken(String token) {
        return (
        		!isTokenExpired(token)
                && VERSION.equals(getVersionFromToken(token))
        );
    }
}
